package com.h3c.ptability.config;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.eos.common.constant.Constant;
import com.eos.common.constant.StatusCodeEnum;
import com.eos.common.custom.BusinessException;
import com.eos.common.domain.ActiveUser;
import com.eos.common.util.IdWorker;
import com.eos.common.util.Result;
import com.h3c.ptability.entity.SdOperLog;
import com.h3c.ptability.service.CommService;
import com.h3c.ptability.service.ManuTranscationService;
import com.h3c.ptability.utils.AppBusinessException;
import com.h3c.ptability.utils.BusiEnumDefine;
import com.h3c.ptability.utils.CommonUtil;
import com.h3c.ptability.utils.PmJwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.util.NestedServletException;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map;
import java.util.Objects;

@Slf4j
public class PTRequestFilter implements Filter {


    private CommService commService;
    private ManuTranscationService manuTranscationService;
    private PathMatcher pathMatcher;
    private String appkey;

    private final ArrayList<String> jwtFreeList = new ArrayList<>();
    private final ArrayList<String> reserveStaffAllowList = new ArrayList<>();
    private final ArrayList<String> authFreeList = new ArrayList<>();

    private String authFree;

    private String jwtFree;

    private String outerApplicationKey;

    private String ssoKey;

    private String pmPublickey;

    ValueOperations<String, Object> valueOperations;

    public PTRequestFilter(String appkey, String ssoKey, String pmPublickey, String outerApplicationKey,
                           String jwtFree, String authFree,
                           CommService commService,
                           ValueOperations<String, Object> valueOperations,
                           ManuTranscationService manuTranscationService) {
        this.ssoKey = ssoKey;
        this.outerApplicationKey = outerApplicationKey;
        this.jwtFree = jwtFree;
        this.authFree = authFree;
        this.pmPublickey = pmPublickey;
        this.commService = commService;
        this.valueOperations = valueOperations;
        this.manuTranscationService = manuTranscationService;

        this.pathMatcher = new AntPathMatcher();

        this.appkey = appkey;
        pathMatcher = new AntPathMatcher();
        authFreeList.add("/authority/service");
        if (!StringUtils.isEmpty(authFree)) {
            String[] result = authFree.split(",");
            int sum = result.length;
            for (int i = 0; i < sum; i++) {
                if (!StringUtils.isEmpty(result[i].trim())) {
                    authFreeList.add(result[i].trim());
                }
            }
        }
        jwtFreeList.add("/webjars/**");
        jwtFreeList.add("/electronic/callback");
        jwtFreeList.add("/druid/**");
        jwtFreeList.add("/doc**");
        jwtFreeList.add("/v2/**");
        jwtFreeList.add("/favicon**");
        jwtFreeList.add("/service-worker**");
        jwtFreeList.add("/error");
        jwtFreeList.add("/healthy");
        jwtFreeList.add("/healthy/liveness");
        jwtFreeList.add("/healthy/readiness");
        jwtFreeList.add("/email/sendEmailFile");
        jwtFreeList.add("/bdPost/getPostNameByPostId");
        jwtFreeList.add("/ptUploadFile/getSfsPicture");
        jwtFreeList.add("/ptUploadFile/downloadFileEncryption");


        //保存基础信息
        reserveStaffAllowList.add("/ptUploadFile/downloadFile");
        reserveStaffAllowList.add("/ptUploadFile/uploadFile");
        reserveStaffAllowList.add("/ptUploadFile/previewUploadFile");
        reserveStaffAllowList.add("/ptEnum/getAllEnumByAppId");
        reserveStaffAllowList.add("/ptMsgTemplate/getMsgTemplateByType");
        if (!StringUtils.isEmpty(jwtFree)) {
            String[] result = jwtFree.replace(";", ",").split(",");
            int sum = result.length;
            for (int i = 0; i < sum; i++) {
                if (!StringUtils.isEmpty(result[i].trim())) {
                    jwtFreeList.add(result[i].trim());
                }
            }
        }
    }

    public Boolean freeCheckJwt(String lookupPath, String header) throws Exception {
        if (Objects.equals(lookupPath, "/healthy")) {
            return true;
        }
        //系统免认证
        for (String pattern : jwtFreeList) {
            if (pathMatcher.match(pattern, lookupPath)) {
                return true;
            }
        }
        //系统免授权
        for (String pattern : authFreeList) {
            if (pathMatcher.match(pattern, lookupPath)) {
                return true;
            }
        }

        if (!StringUtils.hasText(header)) {
            log.error("jwtFreeList:{};lookupPath:{}", jwtFreeList, lookupPath);
        }

        //验证得到头信息
        if (!header.startsWith("Bearer ")) {
//            header = URLDecoder.decode(header);
//            throw new BusinessException(StatusCodeEnum.UN_AUTH);
            //非用户域账号请求，是其他应用调用绩效的服务，检查header中的Authorization配置的用户名密码是否在我的系统配置中
            if (!StringUtils.isEmpty(outerApplicationKey)
                    && !outerApplicationKey.equals("empty")
                    && outerApplicationKey.indexOf(header) >= 0
                    && header.indexOf(":")> 0) {
                ActiveUser activeUser = CommonUtil.userHolder.get();
                if (activeUser == null) {
                    activeUser = new ActiveUser();
                }
                activeUser.setUserId("app-"+ header.split(":")[0]);
                return true;
            } else {
                throw new BusinessException(StatusCodeEnum.UN_AUTH);
            }
        } else {
            //得到token
            String token = header.substring(7);
            if (StringUtils.isEmpty(token)) {
                throw new BusinessException(StatusCodeEnum.TOKEN_NOT_FIND);
            }
            //判断是否是预备员工，是的话权限校验
            Claims claims = PmJwtUtil.getClaims(token, pmPublickey);
            if (claims == null) {
                throw new BusinessException(StatusCodeEnum.TOKEN_FAIL);
            }
            Boolean reserveStaff = claims.get("reserveStaff", Boolean.class);
            if(Objects.nonNull(reserveStaff) && reserveStaff.equals(true)){
                if(!reserveStaffAllowList.contains(lookupPath)){
                    throw new BusinessException(StatusCodeEnum.UN_AUTH);
                }
            }
            //userId 域账号
            String userId = claims.getId();
            if (StringUtils.isEmpty(userId)) {
                throw new BusinessException(StatusCodeEnum.TOKEN_FAIL);
            }
            ActiveUser activeUser = CommonUtil.userHolder.get();
            if (activeUser == null) {
                activeUser = new ActiveUser();
            }
            activeUser.setUserId(userId);
            return true;
        }
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        long startTime = System.currentTimeMillis();
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
        String url = httpServletRequest.getRequestURL().toString();
        CustomHttpServletRequestWrapper customerRequestWrapper = null;
        if (url.indexOf("import") < 0 && url.indexOf("File") < 0) {
            customerRequestWrapper = new CustomHttpServletRequestWrapper(httpServletRequest);
        }

        String realIP = CommonUtil.getIp(httpServletRequest);
        String lookupPath = httpServletRequest.getRequestURI().trim();
        String headerAuth = httpServletRequest.getHeader(Constant.AUTHORIZATION);

        if (CommonUtil.userHolder.get() != null) {
            CommonUtil.userHolder.remove();
        }
        CommonUtil.userHolder.set(ActiveUser.builder().realIp(realIP).build());
        Boolean options = false;
        if (httpServletRequest.getMethod().equals(Constant.OPTIONS)) {
            options = true;
        }
        try {
            if (options || freeCheckJwt(lookupPath, headerAuth)) {
                SdOperLog requestLog = new SdOperLog();
                String remoteAddr = realIP + ":" + httpServletRequest.getRemotePort();
                boolean recordFlag = true;
                boolean saveParmFlag = false;
                if (url.contains("/healthy")
                        || url.contains("/token")
                        || url.contains("/electronic")
                        || url.contains("/static")
                        || url.contains("/favicon")) {
                    recordFlag = false;
                }
                if (url.contains("/get")
                        || url.contains("/query")
                        || url.contains("/find")
                        || url.contains("/select")) {
                    saveParmFlag = true;
                }

                if (recordFlag) {
                    requestLog.setOperId(IdWorker.nextId() + "");
                    String loginUserId = commService.getLoginUserIdWithNoException();
                    if (StringUtils.isEmpty(loginUserId)) {
                        loginUserId = "unknown";
                    }
                    requestLog.setOperUserId(loginUserId);
                    requestLog.setOperUserName(commService.getLoginUserNameWithNoException());
                    requestLog.setRequestUrl(url);
                    if (saveParmFlag) {
                        if (customerRequestWrapper != null) {
                            Map<String, String[]> parameterMap = servletRequest.getParameterMap();
                            String reqParam = JSONObject.toJSONString(parameterMap);
                            if (parameterMap != null && parameterMap.size() > 0) {
                                if (reqParam.length() > 1000) {
                                    requestLog.setRequestParam(reqParam.substring(0, 1000));
                                } else {
                                    requestLog.setRequestParam(reqParam);
                                }
                            } else {
                                requestLog.setRequestParam(CommonUtil.getPostData(customerRequestWrapper));
                            }
                        }
                    }

                    requestLog.setRequestTime(new Date());
                    requestLog.setClientIp(remoteAddr);

                    try {
                        if (customerRequestWrapper == null) {
                            filterChain.doFilter(servletRequest, servletResponse);
                        } else {
                            filterChain.doFilter(customerRequestWrapper, servletResponse);
                        }
                        requestLog.setRequestResult(BusiEnumDefine.FLAG_YES);
                        Long endTime = System.currentTimeMillis();
                        requestLog.setCostMs(endTime - startTime);
//                        httpServletRequest.getMethod();
                        manuTranscationService.doSaveInventoryRequestLog(requestLog, options);
                    } catch (Exception e) {
                        requestLog.setRequestResult(BusiEnumDefine.FLAG_NO);
                        Long endTime = System.currentTimeMillis();
                        requestLog.setCostMs(endTime - startTime);
                        manuTranscationService.doSaveInventoryRequestLog(requestLog, options);
                        throw e;
                    }
                } else {
                    if (customerRequestWrapper == null) {
                        filterChain.doFilter(servletRequest, servletResponse);
                    } else {
                        filterChain.doFilter(customerRequestWrapper, servletResponse);
                    }
                }

            } else {
                throw new AppBusinessException("您没有权限访问当前服务");
            }
        } catch (Exception e) {
            if (e instanceof NestedServletException) {
                if (e.getCause() instanceof Error) {

                }
                if (e.getCause() instanceof Exception) {
                    e = (Exception) e.getCause();
                }
            }
            e.printStackTrace();
            httpServletResponse.setCharacterEncoding("UTF-8");
            httpServletResponse.setStatus(200);
            httpServletResponse.setContentType("application/json; charset=utf-8");
            PrintWriter out = httpServletResponse.getWriter();
            Result result = new Result(false, StatusCodeEnum.UNKNOW_ERROR, e.toString());
            if (e instanceof BusinessException) {
                BusinessException be = (BusinessException) e;
                result = be.getResult();
            }
            result.setMessage(appkey + " " + result.getMessage());
            out.append(JSONObject.toJSONString(result, SerializerFeature.WriteMapNullValue));
        }
        CommonUtil.userHolder.remove();
    }


}

